﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using PhiStore.Packages.InternalPackages;
using PhiStore.Threading;

namespace PhiStore.Communication
{
    internal class ConnectionManager : Boostable
    {
        private TcpListener listener = null;
        private Channel<int> portModifier;
        private int port;
        
        public ConnectionManager(Channel<int> portModifier)
        {
            Log.Instance.AddLogEntry("ConnectionManager", "Initializing");
            this.portModifier = portModifier;
        }

        ~ConnectionManager()
        {
            try
            {
                if (listener != null)
                    listener.Stop();
                if (portModifier != null)
                    portModifier.Poison();
            }
            catch (Exception) { }
        }

        public void Run()
        {
            Log.Instance.AddLogEntry("ConnectionManager", "Starting TCP listening");

            port = portModifier.Receive();//halts until a portnumber is received

            ActivityMonitor.Instance.Reset();

            bool GCing = false;
            try
            {
                listener = new TcpListener(IPAddress.Any, port);
                listener.Start();

                DateTime lastGC = DateTime.Now;
                TimeSpan GCInterval = new TimeSpan(1, 0, 0); //1h

                while (!portModifier.IsPoisoned)
                {
                    while (!listener.Pending() && !portModifier.IsPoisoned) //Idle loop
                    {
                        if (portModifier.Count > 0)// we should start listening on a different port
                        {
                            listener.Stop();
                            listener = new TcpListener(IPAddress.Any, portModifier.Receive());
                            listener.Start();
                        }
                        else if (ActivityMonitor.Instance.HasActivity() || GCing) // server is busy - no GC
                        {
                            Thread.Sleep(100);

                            if (!ActivityMonitor.Instance.HasActivity() && lastGC.Add(GCInterval) < DateTime.Now)
                            { //it's been a while since last gc
                                lastGC = DateTime.Now;
                                PhiServer.Instance.ObjectManagerChannel.Send(new GarbageCollect());
                            }
                        }
                        else if (!GCing) //server is idle
                        {
                            PhiServer.Instance.ObjectManagerChannel.Send(new GarbageCollect());
                            lastGC = DateTime.Now;
                            GCing = true;
                        }
                    }

                    if (listener.Pending())
                    {
                        if (GCing)
                        {
                            PhiServer.Instance.ObjectManagerChannel.Send(new StopGarbageCollect());
                            GCing = false;
                        }

                        TcpClient client = listener.AcceptTcpClient();

                        Worker worker = new Worker(client);
                        Booster.Boost(worker);
                        ActivityMonitor.Instance.IncreaseActivity();
                    }
                }
            }
            catch (SocketException e)
            {
                PhiServer.Instance.Stop();
                Log.Instance.AddLogEntry("ConnectionManager", "Socket Exception: " + e.ToString());
            }
            catch(Exception e)
            {
                PhiServer.Instance.Stop();
                Log.Instance.AddLogEntry("ConnectionManager", "Exception: " + e.ToString());
            }

            if (listener != null) listener.Stop();

            Log.Instance.AddLogEntry("ConnectionManager", "Stopped TCP listening");
        }
    }
}
